﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;


namespace Scheduler
{
    /**Class AcctManagement
     * @author Sara Tuohy
     * @version 1.0
     * Course : ITEC 4860 Spring, 2012
     * Written: March, 2012
     * This class manages the GUI that handles adding/deactivating Employees and
     * Resetting Employee passwords
     * Manages the AcctManagemet GUI
     * Update: implement password reset, refine EmployList / SelectionList / ComboBoxes
     * to ensure the selected indices match.
     */

    public partial class AcctManagement : Form
    {
        /**
         * Boolean for temporary EmpDay bug temporarily disables adding Employees           */
        private bool bugWorkAround = false;

        /**
         * Booleans to control logic flow; notCancelled for add new employee logic          */
        private bool notCancelled = true;
        /**
         * Booleans to control logic flow; employeeAdded for add new employee logic          */
        private bool employeeAdded;
        //private const string coId = "GGCSWProj";
        /**
         * Access to Data Base for table update                  */
        private Data_Access_Layer.AcctManagement acctMgrUpdater;
        /**
         * Access to Data Base for table update                  */
        private Data_Access_Layer.Employee employeeData;
        /*
         * List of all employees names                          */
        private Business_Logic_Layer.Employee[] EmployeeList;
        /*
         * List of all employees                                */
        private string[] EmployeeSelectionList;
        /*
         * PayRate displayed information only                   */
        private decimal empPayRate;

        /**Method: AcctManagement
         * Constructor
         */
        public AcctManagement()
        {
            InitializeComponent();
            employeeAdded = false;
            setup();
            if (bugWorkAround)
                disableAdd();
        }

        /**Method disableAdd
         * Temporarily disable adding new employees
         */
        private void disableAdd()
        {
            //combo boxes
            amAcctTypeComboBox.Enabled = false;
            //Text boxes
            amAddLastNameTB.Enabled = false;
            amAddFirstNameTB.Enabled = false;
            amMITextBox.Enabled = false;
            amAddEmailTB.Enabled = false;
            amAddTempPWTB.Enabled = false;
            amRetypePWTB.Enabled = false;
            //Masked Text Boxes
            amPayRateMaskedTextBox1.Enabled = false;
            amPhoneMaskedTextBox.Enabled = false;
            amAddUserMaskedTextBox.Enabled = false;
            //Buttons
            amAddNewCancelButton.Enabled = false;
            amAddNewSaveButton.Enabled = false;
        }

        /**Method: setup
         * Sets up GUI controls
         */
        private void setup()
        {
            if (!Presentation_Layer.GlobalVariables.GlobalEmployeeListCompiled)
            {
                Business_Logic_Layer.GlobalDataManager globalDataMgr = new Business_Logic_Layer.GlobalDataManager();
                globalDataMgr.CreateEmployeeList();
            }
            //Cast the returned list to an array of Employee
            EmployeeList = (Business_Logic_Layer.Employee[])
                Presentation_Layer.GlobalVariables.GlobalEmployeeList;
            Array.Sort(EmployeeList);                                   //added after implementing IComparable 04062012
            EmployeeSelectionList = new string[EmployeeList.Length];
            int i = 0;
            foreach (Business_Logic_Layer.Employee emp in EmployeeList)
            {
                //Modified to accommodate duplicate names; IDs (now included) must be unique
                EmployeeSelectionList[i] = emp.ToString();
                i++;
            }
            amDeactivateEmpComboBox.Items.AddRange(EmployeeSelectionList);
            amPWResetSelComboBox.Items.AddRange(EmployeeSelectionList);
        }

        /**Method dispEmpList
         * Display list with indices.  Not currently called from anywhere, but 
         * is very useful if there is any list/selection match errors
         * Purpose: diagnostic tool 
         */
        private void dispEmpList(string[] empList)
        {
            string msg = "";
            for (int i = 0; i < empList.Length; i++)
            {
                msg += i + ". " + empList[i] + "\n";
            }
            MessageBox.Show(msg);
        }

        /**Method amAddNewSaveButton_Click
         * Validates the data input to "Add New Employee" function
         * Purpose: to save the data to the DB after it's validated
         */
        private void amAddNewSaveButton_Click(object sender, EventArgs e)
        {
            bool unValidatedInput = true;

            while (unValidatedInput && notCancelled)
            {
                unValidatedInput = invalidInput(amAddLastNameTB.Text,
                                                    amAddFirstNameTB.Text,
                                                    amPhoneMaskedTextBox.Text,
                                                    amAddEmailTB.Text,
                                                    amAddUserMaskedTextBox.Text,
                                                    amAcctTypeComboBox.Text,
                                                    amAddTempPWTB.Text,
                                                    amRetypePWTB.Text,
                                                    amPayRateMaskedTextBox1.Text);

            }
            if (!notCancelled)              //if we were cancelled out of this transaction, 
                notCancelled = true;        //reset the notCancelled check for next time.
            if (!unValidatedInput)          //Format Employee name: Last, First M.
            {
                string employeeName;
                if (amMITextBox.Text.Equals(""))
                {
                    employeeName = amAddLastNameTB.Text + ", " + amAddFirstNameTB.Text;
                }
                else
                {
                    employeeName = amAddLastNameTB.Text + ", " + amAddFirstNameTB.Text + " " +
                                    amMITextBox.Text + ". ";
                }
                if (employeeName.Length > 40)
                {
                    employeeName = employeeName.Substring(0, 40);
                }
                saveData(employeeName);
            }
        }

        /**Method: saveData
         * Save data from Add Employee function
         * Purpose: saves all new accts to the User Table; saves employee accts to the 
         * employee table and manager accts to the manager table
         * @param string empName name of employee to be added
         */
        private void saveData(string empName)
        {
            string uid = amAddUserMaskedTextBox.Text;
            acctMgrUpdater = new Data_Access_Layer.AcctManagement(uid);
            bool acctFound = acctMgrUpdater.locateUser();
            if (!acctFound)
            {
                acctMgrUpdater.createUser(amAddUserMaskedTextBox.Text, amAddTempPWTB.Text,
                                    false, true, empName,
                                    amPhoneMaskedTextBox.Text,
                                    amAddEmailTB.Text);
                switch (amAcctTypeComboBox.SelectedIndex)
                {
                    case 0: addEmpToDB();
                        break;
                    case 1: addMgrToDB();
                        break;
                    default: MessageBox.Show("Must Choose Account Type ");
                        break;
                }       //end of switch on account type
            }           //end of if account not found
            else
            {
                MessageBox.Show("Attempt to add " + uid + " failed; duplicate ID found.",
                                "Add Failed", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            if (employeeAdded)
            {
                setDefaultComment();
            }
        }

        /**Method: refreshEmployeeLists
         * Sets global 'ListCompiled' to false and calls setup again
         * Purpose: to refresh the selections lists with the new data.  This refreshes the lists used by this panel,
         * and since all the other panels go by the global list, it refreshes them as well.
         */
        private void refreshEmployeeLists()
        {
            Presentation_Layer.GlobalVariables.GlobalEmployeeListCompiled = false;
            amDeactivateEmpComboBox.Items.Clear();
            amPWResetSelComboBox.Items.Clear();
            setup();
        }

        /**Method: addEmpToDB
         * Update DB
         * Purpose: add employee to employee table in the DB
         */
        private void addEmpToDB()
        {
            DateTime hireDate = DateTime.Today;
            string preferencesLastUpdated = "20120101000000";
            string empID = amAddUserMaskedTextBox.Text;
            acctMgrUpdater = new Data_Access_Layer.AcctManagement(empID);
            //fields are payrate, hiredate, comment, comment, two sets of max/min hours, and A DateTime for
            //when this employee last updated preferences that will prompt him to update upon entry.
            acctMgrUpdater.createEmployee(empPayRate, hireDate, " ", " ", 0, 0, 0, 0, preferencesLastUpdated);
            addEmpDays(empID);
            employeeAdded = true;
            refreshEmployeeLists();
        }
        /**method addEmpDays
         * When a new employee is added to the DB, "EmployeeDays" (key=ID + Day) need to be added
         * to the EmpDayInfo table
         * @param string employeeID
         */ 
        private void addEmpDays(string employeeID)
        {
            ArrayList errors;
            Data_Access_Layer.EmpDayInfo empDay = new Data_Access_Layer.EmpDayInfo(employeeID, "Sunday");
            foreach (string day in Presentation_Layer.GlobalVariables.GlobalWeekDays)
            {
                errors = empDay.createEmpDay(employeeID, day);
                if (errors.Count > 0)
                    MessageBox.Show("Create day failed for " + employeeID + ", " + day + " Reason: "
                        + errors[0].ToString() + " first of " + errors.Count);
            }
        }

        /**Method: addDefaultComment
         * Add " " to the comment field
         * Purpose: to prevent getting a null back on an employee account
         */
        private void setDefaultComment()
        {
            employeeData = new Data_Access_Layer.Employee(amAddUserMaskedTextBox.Text);
            if (employeeData.locateEmployee())
            {
                employeeData.setEmpComment(" ");
                MessageBox.Show("Employee successfully added.", "Successful Add",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else MessageBox.Show("Employee not Found");
            employeeAdded = false;                          //reset in case of future adds!
        }

        /**Method: addMgrToDB
         * Update DB
         * Purpose: add manager to manager table in the DB
         */
        private void addMgrToDB()
        {
            string mgrID = amAddUserMaskedTextBox.Text;
            acctMgrUpdater = new Data_Access_Layer.AcctManagement(mgrID);
            acctMgrUpdater.createManager(Presentation_Layer.GlobalVariables.GlobalCompanyID);
            MessageBox.Show("Manager successfully added.", "Successful Add",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /**Method invalidInput
         * Validates input data, stops at the first detected error
         * Purpose: validate the user's input
         * @param string last name
         * @param string first name
         * @param string phone
         * @param string email address
         * @param string user name
         * @param string account type
         * @param string password
         * @param string password verify (retyped password
         * @param string pay rate
         * @return bool true if invalid, otherwise false
         */
        private bool invalidInput(string lastName, string firstName, string phoneNum, string eMail,
                                   string userName, string acctType, string password, string passwordVerify, string payStr)
        {
            bool invalid = true;
            if (acctType.Length < 6) errorNotification("Must select account type.");
            else if (lastName.Length < 1) errorNotification(String.Format("Last Name too Short: {0}", lastName));
            else if (firstName.Length < 1) errorNotification(String.Format("First Name too Short: {0}", firstName));
            else if (badPhoneNumber(phoneNum)) errorNotification(String.Format("Incorrect phone number format: {0}", phoneNum));
            else if (emailInvalid(eMail)) errorNotification(String.Format("Invalid EMail format: {0}", eMail));
            else if (userName.Length < 1) errorNotification(String.Format("User name cannot be blank"));
            else if (password.Length < 1) errorNotification("Password cannot be blank.");
            else if (passwordMismatch(password, passwordVerify)) errorNotification("Error password mismatch");
            else if (employeePayRateInvalid(acctType, payStr))
                errorNotification(String.Format("Employee Account requires valid pay rate: {0}", payStr));
            else invalid = false;
            return invalid;
        }

        /**Method: badPhoneNumber
         * Validates Phone Number
         * Purpose:ensure all fields of phone number have valid data
         * @param string phone number
         * @return bool true if bad, otherwise false
         */
        private bool badPhoneNumber(string phone)
        {
            if (amPhoneMaskedTextBox.Text.Length < 12)
                return true;
            string acString, prfxString, numberString;
            acString = amPhoneMaskedTextBox.Text.Substring(0, 3);
            prfxString = amPhoneMaskedTextBox.Text.Substring(4, 3);
            numberString = amPhoneMaskedTextBox.Text.Substring(8, 4);
            int number;
            bool isBad = true;
            try
            {
                number = Convert.ToInt16(acString);
                number = Convert.ToInt16(prfxString);
                number = Convert.ToInt16(numberString);
                isBad = false;
            }
            catch (FormatException)
            {
                isBad = true;
            }
            return isBad;
        }

        /**Method: emailInvalid
         * validates email address
         * Purpose: checks for simple typos in email
         * @param string email Address
         * @return bool true if invalid, otherwise false
         */
        private bool emailInvalid(string emailAddr)
        {
            int idx = emailAddr.IndexOf("@");
            if (idx < 0) return true;
            if (emailAddr.Length < 6) //minimum=1 char user, "@", at least 1.2 = 6 characters minimum!
                return true;
            return false;
        }

        /**Method: passwordMismatch
         * Compares the password to the retyped password
         * Purpose: ensure the password entered is what the user intended; it is unlikely the user
         * would type the WRONG password exactly the same way twice
         */
        private bool passwordMismatch(string password, string pwRetype)
        {
            return !password.Equals(pwRetype, StringComparison.Ordinal);
        }

        /**Method: employeePayRateInvalid
         * Checks the entry is a valid currency type number
         * Purpose: ensure a valid pay rate is entered
         */
        private bool employeePayRateInvalid(string acType, string payS)
        {
            if (acType.Equals("Manager")) return false;
            double decimalPay;
            bool payRateValid;
            try
            {
                decimalPay = Convert.ToDouble(payS);
                empPayRate = Convert.ToDecimal(payS);
                payRateValid = false;
            }
            catch (FormatException)
            {
                payRateValid = true;
            }
            //forgo overflow exception; value came from a MaskedTextBox
            return payRateValid;
        }

        /**Method: errorNotification
         * Indicate an error and update "notCancelled" sentinel
         * Purpose: reusable code that alerts user to an error and sets the 
         * bool 'notCancelled' to false in order to exit the save loop above.
         * @param string errorMsg description of error
         */
        private void errorNotification(string errorMsg)
        {

            MessageBox.Show(errorMsg, "Save Error!",
                  MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            notCancelled = false;
        }

        /**Method: amAddNewCancelButton
         * Lets the user exit the add function without saving data
         * Purpose: supply user an way to exit if he wants to
         * Update: the cancel button now clears the input fields
         */
        private void amAddNewCancelButton_Click(object sender, EventArgs e)
        {
            notCancelled = false;
            //Clear all the fields Per Scrumy 04052011
            amAcctTypeComboBox.SelectedIndex = -1;
            amAddLastNameTB.Clear();
            amAddFirstNameTB.Clear();
            amMITextBox.Clear();
            amPayRateMaskedTextBox1.Clear();
            amAddEmailTB.Clear();
            amPhoneMaskedTextBox.Clear();
            amAddUserMaskedTextBox.Clear();
            amAddTempPWTB.Clear();
            amRetypePWTB.Clear();
        }

        /**Method: amDeactEmpButton
         * Lets the user confirm and deactivate an account
         */
        private void amDeactEmpButton_Click(object sender, EventArgs e)
        {
            int selectedEmployee = amDeactivateEmpComboBox.SelectedIndex;
            Business_Logic_Layer.Employee employee = EmployeeList[selectedEmployee];
            string confirmationInformation = formatConfirmInfo(employee);
            if (MessageBox.Show(confirmationInformation, "Verify Account to Deactivate",
                MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
            {
                acctMgrUpdater = new Data_Access_Layer.AcctManagement(employee.EmpID.Trim());
                if (acctMgrUpdater.locateUser())
                {
                    acctMgrUpdater.setIsAcctActive(false);
                    confirmationInformation = employee.EmpID + ", " + employee.Name + " account deactivated.";
                    MessageBox.Show(confirmationInformation, "Account Deactivated");
                }
                else
                {
                    confirmationInformation = "Employee Locate Failed";
                    MessageBox.Show(confirmationInformation);
                }
            }
            else
            {
                confirmationInformation = "Operation Cancelled";
                MessageBox.Show(confirmationInformation);
            }
            resetButtons(1, false);
        }

        /**Method: resetButtons
         * Reset combo boxes, enable/disable buttons, clear text boxes
         * Purpose: was needed in several places, good thing to write once / use many
         * @param int section 1 = Deactivate Employee Section, 2 = Reset Password Section
         * @param bool newVal button.enabled property will be set to this value
         */
        private void resetButtons(int section, bool newVal)
        {
            switch (section)
            {
                case 1:
                    if (!newVal)
                    {
                        amDeactivateEmpComboBox.SelectedIndex = -1;
                    }
                    amDeactEmpButton.Enabled = newVal;
                    amDeactEmpCancelButton.Enabled = newVal;
                    break;
                case 2:
                    if (!newVal)                //if disabling the buttons, clear the text boxes
                    {
                        amPWResetSelComboBox.SelectedIndex = -1;
                        amPWResetTempPWTB.Clear();
                        retypeResetPWTB.Clear();
                    }
                    empPWResetButton.Enabled = newVal;
                    amPWResetCancelButton.Enabled = newVal;
                    break;
                default:
                    MessageBox.Show("Code not found for section = " + section, "Control Reset Error",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
            }
        }

        /**Method: formatConfirmInfo
         * Format string for MessageBox confirmation
         */
        private string formatConfirmInfo(Business_Logic_Layer.Employee employee)
        {
            string confirmationInformation = "Name: " + employee.Name + "\n" +
            "Phone: " + employee.Phone + "\n" +
            "Email: " + employee.Email + "\n" +
            "Employee ID: " + employee.EmpID + "\n" + "\n" +
            "Is this the account you wish to deactivate? ";
            return confirmationInformation;
        }

        /**Method: amDeactEmpCancelButton_Click
         * If the cancel button is clicked, reset the dropdown to noone selected
         */
        private void amDeactEmpCancelButton_Click(object sender, EventArgs e)
        {
            resetButtons(1, false);
        }

        /**Method: amPWResetCandidateSelected
         * Invoked when a selection is made from the employee list drop down box
         * in the password reset option.
         */
        private void amPWResetCandidateSelected(object sender, EventArgs e)
        {
            //resetButtons sets selected index, changing index calls resetButtons ... well, you see what could happen ...
            if (amPWResetSelComboBox.SelectedIndex != -1)
            {
                resetButtons(2, true);
            }
        }

        /**Method: amDeactCandidateSelected
         * Invoked when a selection is made from the employee list drop down box
         * in the Deactivate employee option.
         */
        private void amDeactCandidateSelected(object sender, EventArgs e)
        {
            //resetButtons sets selected index, changing index calls resetButtons ... well, you see what could happen ...
            if (amDeactivateEmpComboBox.SelectedIndex != -1)
            {
                resetButtons(1, true);
            }
        }

        /**Method: amPWResetCancelButton_Click
         * Clears all fields if user decides against resetting the password
         */
        private void amPWResetCancelButton_Click(object sender, EventArgs e)
        {
            resetButtons(2, false); ;
        }

        /**Method: empPWResetButton_Click
         * Button was enabled when a selection was made from the password reset drop down box
         * Purpose: allow manager to reset password (and unlock account) for an employee
         * Confirmation box ensures there hasn't be a glitch between the index of the employee selection list
         * and the EmployeeList.
         */
        private void empPWResetButton_Click(object sender, EventArgs e)
        {
            string newPassword = amPWResetTempPWTB.Text;
            if (newPassword.Length < 1)
            {
                errorNotification("Password cannot be blank.");
            }
            else if (passwordMismatch(newPassword, retypeResetPWTB.Text))
            {
                MessageBox.Show("Passwords do not match; please retype and try again.",
                    "Password Mismatch Error",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                updatePassword(newPassword);
                resetButtons(2, false);
            }
        }

        /**Method: updatePassword
         * Take the validated information and update the 
         * database entry.
         * @param string newPW new password
         */
        private void updatePassword(string newPW)
        {
            //string name = amPWResetSelComboBox.Text;
            //int idx = Array.IndexOf(EmployeeSelectionList, name);
            int idx = amPWResetSelComboBox.SelectedIndex;
            string confirmInfo = "Reset password for: " + EmployeeList[idx].ToString();
            string empID = EmployeeList[idx].EmpID.Trim();
            if (MessageBox.Show(confirmInfo, "Verify Account to Reset",
                MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
            {
                Data_Access_Layer.AcctManagement acctMgr = new Data_Access_Layer.AcctManagement(empID);
                if (acctMgr.locateUser())
                {
                    acctMgr.setPassword(newPW);
                    acctMgr.setIsAcctLocked(false);
                    MessageBox.Show("Password Reset Successfully");
                    resetButtons(2, false);
                }           //end if user located
                else
                {
                    MessageBox.Show("Employee Locate Failed");
                }           //end else employee not found
            }               //end if password reset confirmed
            else
            {
                MessageBox.Show("Password Reset Cancelled");
            }               //end if not confirmed, cancel the operation
        }                   //end of updatePassword(newPassword);


    }                       //end of form
}                           //end of class
